package com.hjx.windowtool.face;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.wb.swt.SWTResourceManager;

import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.FunctionConfiguration;
import com.arcsoft.face.Rect;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.hjx.windowtool.db.Tool;
import com.hjx.windowtool.util.ImgUtil;

import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.List;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import javax.swing.JFileChooser;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Text;

@Tool("人脸查找")
public class FaceDatabaseFindApp {

	protected Shell shell;
	private List list;
	Map<String,Object> mapFaceFeature=new HashMap<>();
	Map<String,File> mapFiles=new HashMap<>();
	private Label lbl_face;
	private Label lbl_image;
	private Text txt_image;
	private FaceEngine faceEngine;
	private Text lbl_score;
	
	ExecutorService threadPool = Executors.newFixedThreadPool(300);
	private String filter;
	private Text text;
	private FaceFeature faceFeature;
	private Text lbl_msg;
	private List list_result;
	private Button button_5;

	/**
	 * Launch the application.
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			FaceDatabaseFindApp window = new FaceDatabaseFindApp();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open the window.
	 */
	public void open() {
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents() {
		shell = new Shell();
		shell.setSize(979, 523);
		shell.setText("SWT Application");
		shell.setLayout(new GridLayout(2, false));
		
		list = new List(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
		list.setItems(new String[] {"数据加载中"});
		list.addSelectionListener(new SelectionListener() {
			
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					System.out.println(list.getSelectionIndex());
					String item = list.getItem(list.getSelectionIndex());
					System.out.println(item);
					lbl_msg.setText(item);
					File file = mapFiles.get(item);
					ImgUtil.setBackgroundImage(lbl_face, Files.readAllBytes(file.toPath()));
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
			
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				
			}
		});
		GridData gd_list = new GridData(SWT.LEFT, SWT.FILL, false, true, 1, 1);
		gd_list.widthHint = 162;
		list.setLayoutData(gd_list);
		
		Composite composite = new Composite(shell, SWT.NONE);
		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		
		Button button = new Button(composite, SWT.NONE);
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				button.setEnabled(false);
				initService();
			}
		});
		button.setBounds(35, 41, 80, 27);
		button.setText("启动");
		
		Button button_1 = new Button(composite, SWT.NONE);
		button_1.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				String image = txt_image.getText();
				if (image==null) {
					return;
				}
				compareFace(image);
			}
		});
		button_1.setBounds(129, 41, 80, 27);
		button_1.setText("测试");
		
		lbl_face = new Label(composite, SWT.BORDER);
		lbl_face.setText("图片");
		lbl_face.setBounds(10, 145, 300, 300);
		
		lbl_image = new Label(composite, SWT.BORDER);
		lbl_image.setText("图片");
		lbl_image.setBounds(316, 145, 300, 300);
		
		txt_image = new Text(composite, SWT.BORDER);
		txt_image.setBounds(10, 12, 606, 23);
		DropTarget  target=new DropTarget(txt_image,DND.DROP_MOVE|DND.DROP_COPY);
		target.setTransfer(new Transfer[] {FileTransfer.getInstance()});
		target.addDropListener(new DropTargetAdapter() {
			@Override
			public void drop(DropTargetEvent event) {
				String[] data=(String[]) event.data;
				txt_image.setText(data[0]);
			}
		});
		
		lbl_score = new Text(composite, SWT.NONE);
		lbl_score.setBounds(316, 115, 300, 17);
		lbl_score.setText("New Label");
		
		Button button_2 = new Button(composite, SWT.NONE);
		button_2.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				FileDialog fd = new FileDialog(shell);
				String f = fd.open();
				if (f==null) {
					return;
				}
				txt_image.setText(f);
				
			}
		});
		button_2.setBounds(622, 12, 80, 27);
		button_2.setText("选择");
		
		text = new Text(composite, SWT.BORDER);
		text.setBounds(10, 115, 73, 23);
		
		Button button_3 = new Button(composite, SWT.NONE);
		button_3.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				filter=text.getText();
				refresh();
			}
		});
		button_3.setBounds(89, 112, 80, 27);
		button_3.setText("查找");
		
		Button button_4 = new Button(composite, SWT.NONE);
		button_4.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					if (faceFeature==null) {
						return;
					}
					String name = text.getText();
					if (name==null||name.isEmpty()) {
						return;
					}
					for (int i = 0; i < 10000; i++) {
						if(mapFaceFeature.containsKey(name+".jpg")) {
							if (Arrays.equals(faceFeature.getFeatureData(), (byte[]) mapFaceFeature.get(name+".jpg"))) {
								return;
							}
							name=name+i;
							continue;
						}
						break;
					}
					String imageName = name+".jpg";
					mapFaceFeature.put(imageName, faceFeature.getFeatureData());
					saveModel();
					
					File file = new File("face\\"+imageName);
					Files.copy(new File(txt_image.getText()).toPath(), file.toPath());
					lbl_msg.setText("保存成功");
					mapFiles.put(imageName, file);
				} catch (Exception e1) {
					e1.printStackTrace();
					lbl_msg.setText("保存失败"+e1.getMessage());
				}
			}
		});
		button_4.setBounds(216, 40, 80, 27);
		button_4.setText("保存人脸数据");
		
		lbl_msg = new Text(composite, SWT.NONE);
		lbl_msg.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
		lbl_msg.setBounds(10, 76, 606, 17);
		
		list_result = new List(composite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
		list_result.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					System.out.println(list_result.getSelectionIndex());
					String item = list_result.getItem(list_result.getSelectionIndex());
					System.out.println(item);
					File file = mapFiles.get(item.split(",")[0]);
					ImgUtil.setBackgroundImage(lbl_face, Files.readAllBytes(file.toPath()));
					lbl_score.setText(item);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});
		list_result.setBounds(622, 145, 172, 293);
		
		button_5 = new Button(composite, SWT.NONE);
		button_5.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					System.out.println(list.getSelectionIndex());
					String item = list.getItem(list.getSelectionIndex());
					System.out.println(item);
					File file = mapFiles.get(item);
					mapFaceFeature.remove(item);
					saveModel();
					file.delete();
					lbl_msg.setText("删除成功");
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});
		button_5.setBounds(302, 41, 80, 27);
		button_5.setText("删除人脸");

	}

	protected void compareFace(String image) {
		faceFeature=null;
		System.out.println("总共"+mapFaceFeature.size()+"个人脸");
		File f = new File(image);
		if (!f.exists()) {
			return;
		}
		ImageInfo imageInfo = getRGBData(f);
        java.util.List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
        int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
        System.out.println(faceInfoList);
        if (faceInfoList.isEmpty()) {
			lbl_score.setText("未找到人脸");
			return;
		}
        FaceInfo info=faceInfoList.get(0);
        Rect rect = info.getRect();
        
//        BufferedImage faceImage=new BufferedImage(rect.left-rect.right, rect.top-rect.bottom, BufferedImage.TYPE_INT_ARGB);
//        faceImage.getGraphics().drawim
		faceFeature = new FaceFeature();
        errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
        if(faceFeature.getFeatureData()==null) {
        	lbl_score.setText("提取人脸特征失败");
        	faceFeature=null;
			return;
        }
        System.out.println("特征值大小：" + faceFeature.getFeatureData().length);
        
        AtomicInteger score=new AtomicInteger(-1);
        String name="";
        long currentTimeMillis = System.currentTimeMillis();
        Map<String,Float> mapscore=new HashMap<>();
        for (Entry<String, Object> entry : mapFaceFeature.entrySet()) {
        	byte[] featureData = (byte[]) entry.getValue();
        	
        	//特征比对
            FaceFeature targetFaceFeature = new FaceFeature();
            targetFaceFeature.setFeatureData(faceFeature.getFeatureData());
            FaceFeature sourceFaceFeature = new FaceFeature();
            sourceFaceFeature.setFeatureData(featureData);
            FaceSimilar faceSimilar = new FaceSimilar();

            errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);

//            System.out.println("相似度：" + faceSimilar.getScore());
            if (faceSimilar.getScore()*100>score.get()) {
            	name=entry.getKey();
            	mapscore.put(name, faceSimilar.getScore());
            	score.set((int) (faceSimilar.getScore()*100));
			}
		}
        System.out.println("花费时间："+(System.currentTimeMillis()-currentTimeMillis));
        lbl_score.setText(name+",相似度："+score);
        
        java.util.List<Entry<String,Float>> collect = mapscore.entrySet().stream().collect(Collectors.toList());
        collect.sort((e1,e2)->e2.getValue().compareTo(e1.getValue()));
        java.util.List<String> collect2 = collect.stream().map(e->e.getKey()+",相似度"+e.getValue()).collect(Collectors.toList());
        list_result.setItems(collect2.toArray(new String[collect2.size()]));
        try {
			ImgUtil.setBackgroundImage(lbl_image, Files.readAllBytes(f.toPath()));
		} catch (Exception e3) {
			// TODO Auto-generated catch block
			e3.printStackTrace();
		}
	}

	protected void initService() {
		//从官网获取
        String appId = "GWjGEzy3MuUwASosDD3XWrGqhJeZ6pCSnmDNWcSUTHiw";
        String sdkKey = "9E65RDgrqjV5jcUcctr6MQF1Zyy6vWqQ9dTCRqMkHH2B";

        faceEngine = new FaceEngine(System.getProperty("user.dir")+File.separator+"arcsoft");

        //激活引擎
        int errorCode = faceEngine.activeOnline(appId, sdkKey);
        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            System.out.println("引擎激活失败");
        }
        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        engineConfiguration.setDetectFaceMaxNum(10);
        engineConfiguration.setDetectFaceScaleVal(16);
        //功能配置
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(false);
        functionConfiguration.setSupportFace3dAngle(false);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(false);
        functionConfiguration.setSupportIRLiveness(false);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

        //初始化引擎
        errorCode = faceEngine.init(engineConfiguration);
        if (errorCode != ErrorInfo.MOK.getValue()) {
            System.out.println("初始化引擎失败");
        }
        
        final File file = new File(System.getProperty("user.dir")+File.separator+"face");
        File mfile = new File("model.model");
        if(mfile.exists()) {
        	try(ObjectInputStream bis=new ObjectInputStream(new FileInputStream(mfile))) {
				mapFaceFeature = (Map<String, Object>) bis.readObject();
			} catch (Exception e) {
				e.printStackTrace();
			}
        }
        System.out.println(mapFaceFeature.keySet().size());
        
        
        ReentrantLock lock=new ReentrantLock();
        File[] listFiles = file.listFiles();
        final java.util.List<String> errorName=new CopyOnWriteArrayList<>();
		for (int i = 0; i < listFiles.length; i++) {
			final File f = listFiles[i];
			if(f.isDirectory()) {
				continue;
			}
			final String name = f.getName();
			mapFiles.put(name, f);
			if (mapFaceFeature.containsKey(name)) {
				continue;
			}
			int j=i;
			Runnable runnable = new Runnable() {
				public void run() {
					try {
						System.out.println("处理第"+j+"个人脸");
						//人脸检测
						ImageInfo imageInfo = getRGBData(f);
						java.util.List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
						lock.lock();
						int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
						lock.unlock();
						System.out.println(faceInfoList);
						if(faceInfoList.isEmpty()) {
							try {
								File dest = new File(file,"noface"+File.separator+name);
								dest.getParentFile().mkdirs();
								f.renameTo(dest);
								errorName.add(name);
								Files.write(Paths.get("errorName.txt"), errorName);
							} catch (Exception e3) {
								e3.printStackTrace();
							}
							return;
						}

						//特征提取
						FaceFeature faceFeature = new FaceFeature();
						errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
						if (faceFeature.getFeatureData()!=null) {
							System.out.println(j+"特征值大小：" + faceFeature.getFeatureData().length);
							mapFaceFeature.put(name, faceFeature.getFeatureData());
						}else {
							try {
								errorName.add(name);
								Files.write(Paths.get("errorName.txt"), errorName);
							} catch (Exception e3) {
								e3.printStackTrace();
							}
						}
						if (j > 0 && j % 100 == 0) {
							saveModel(mfile);
						}
					} catch (Exception e) {
						lock.unlock();
						e.printStackTrace();
						try {
							errorName.add(name);
							Files.write(Paths.get("errorName.txt"), errorName);
						} catch (Exception e3) {
							e3.printStackTrace();
						}
					}
				}
			};
			threadPool.submit(runnable);
		}
		threadPool.shutdown();
		System.out.println(threadPool.isTerminated());
		while(!threadPool.isTerminated()) {
			
		}
        saveModel(mfile);
        refresh();
	}

	/**
	 * 
	 */
	public void refresh() {
		System.out.println("人脸库大小"+mapFaceFeature.size());
		java.util.List<String> collect = mapFaceFeature.keySet().stream().collect(Collectors.toList());
		if(filter!=null&&!filter.isEmpty()) {
			collect = mapFaceFeature.keySet().stream().filter(e->e.contains(filter)).collect(Collectors.toList());
		}
        collect.sort((e1,e2)->e1.compareTo(e2));
        list.setItems(collect.toArray(new String[collect.size()]));
	}
	/**
	 * @param file
	 */
	public void saveModel() {
		File file=new File("model.model");
		try (ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(file))){
			oos.writeObject(mapFaceFeature);
			oos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * @param file
	 */
	public void saveModel(File file) {
		try (ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(file))){
			oos.writeObject(mapFaceFeature);
			oos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
